Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use node-level | for efficiency in infix_spaces_linter #2025

Merged
merged 10 commits into from
Aug 2, 2023

Conversation

MichaelChirico
Copy link
Collaborator

Similar to #2024, I found infix_spaces_linter() to be among the slowest, and apparently owing to the //* expression.

It's really slow! Even with 22 entries in //NODE1[...] | //NODE2[...] | ... | //NODE22[...], I find this approach about 3x as fast in the core loop:

exp <- get_source_expressions("QC.R")
ll <- lintr::infix_spaces_linter()
system.time(replicate(100, lapply(exp$expressions, ll)))

## BEFORE
#    user  system elapsed 
# 148.841   0.800 157.128

## AFTER
#    user  system elapsed 
#  48.209   0.286  51.664

@MichaelChirico
Copy link
Collaborator Author

I started a Wiki page here:

https://github.com/r-lib/lintr/wiki/Writing-robust,-performant-linters

My first thought was to add notes like this or an efficiency/"advanced" section to the custom_linters vignette, but I think a Wiki may be preferable here for reasons noted there.

@AshesITR
Copy link
Collaborator

The position() condition breaks your approach unfortunately.
Maybe we can avoid a top-level //* by constructing an XPath that has /*[position() > 1] as an element?

@MichaelChirico
Copy link
Collaborator Author

how about //OP[parent::expr[count(expr) >2]]

untested but hopefully it's clear what I mean -- test against unary operator on parent instead of self

@AshesITR
Copy link
Collaborator

Worth a try.

@MichaelChirico
Copy link
Collaborator Author

MichaelChirico commented Jul 27, 2023

Not quite so simple (h/t our extensive test suite!), but the basic idea works. The complications for future reference were:

  1. parent::expr is not always true, we see <expr_or_assign_or_help> for some EQ_ASSIGN cases
  2. count(expr) > 1 is not enough for the EQ_SUB (assignment of function parameters) case, where a missing argument means there's no <expr> on the RHS, while the LHS doesn't get an <expr> because it's SYMBOL_SUB there, so the only <expr> is for the <expr><SYMBOL_FUNCTION_CALL>alist</SYMBOL_FUNCTION_CALL></expr>

({xp_or(paste0('self::', infix_tokens))})
and position() > 1
xpath <- paste(collapse = "|", glue::glue("//{infix_tokens}[
parent::*[count(expr) + count(SYMBOL_SUB) > 1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is count(expr | SYMBOL_SUB) a thing?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I still don't have a good sense of when | can be used like this. It passed tests locally, so let's go for it!

@codecov-commenter
Copy link

codecov-commenter commented Jul 29, 2023

Codecov Report

Merging #2025 (35a06ab) into main (c3c10bb) will decrease coverage by 0.01%.
The diff coverage is 100.00%.

❗ Current head 35a06ab differs from pull request most recent head 3ad2ec8. Consider uploading reports for the commit 3ad2ec8 to get more accurate results

@@            Coverage Diff             @@
##             main    #2025      +/-   ##
==========================================
- Coverage   98.55%   98.55%   -0.01%     
==========================================
  Files         113      113              
  Lines        4995     4994       -1     
==========================================
- Hits         4923     4922       -1     
  Misses         72       72              
Files Changed Coverage Δ
R/infix_spaces_linter.R 100.00% <100.00%> (ø)

@AshesITR
Copy link
Collaborator

Do we have a test for something like -1-1 to ensure the XPath refactoring scopes correctly and only finds one lint in the example?

Copy link
Collaborator

@AshesITR AshesITR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.

tests/testthat/test-infix_spaces_linter.R Outdated Show resolved Hide resolved
@AshesITR AshesITR merged commit f9bf506 into main Aug 2, 2023
21 checks passed
@AshesITR AshesITR deleted the MichaelChirico-patch-5 branch August 2, 2023 05:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants